package com.hyk.service.impl;

import cn.hutool.core.lang.Snowflake;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.CreateCache;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hyk.domain.Account;
import com.hyk.domain.CashWithdrawAuditRecord;
import com.hyk.domain.CashWithdrawals;
import com.hyk.domain.Coin;
import com.hyk.dto.AdminUserDto;
import com.hyk.dto.UserBankDto;
import com.hyk.dto.UserDto;
import com.hyk.enums.AccountEnums;
import com.hyk.enums.DirectionEnums;
import com.hyk.fegin.AdminUserFegin;
import com.hyk.feign.UserBankFeign;
import com.hyk.feign.UserServiceFeign;
import com.hyk.mapper.CashWithdrawalsMapper;
import com.hyk.service.AccountService;
import com.hyk.service.CashWithdrawAuditRecordService;
import com.hyk.service.CashWithdrawalsService;
import com.hyk.service.CoinService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CashWithdrawalsServiceImpl extends ServiceImpl<CashWithdrawalsMapper, CashWithdrawals> implements CashWithdrawalsService{

    @Autowired
    private UserServiceFeign userServiceFeign;

    @Resource
    private CashWithdrawalsMapper cashWithdrawalsMapper;

    private final Snowflake snowflake = new Snowflake(2,1);


    @CreateCache(name = "CASH_RECHARGE_LOCK:", expire = 100, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.BOTH)
    private Cache<String, String> cache;


    @Resource
    private AdminUserFegin adminUserFegin;

    @Autowired
    private CoinService coinService;

    @Autowired
    private AccountService accountService;

    @Autowired
    private CashWithdrawAuditRecordService cashWithdrawAuditRecordService;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Autowired
    private UserBankFeign userBankFeign;

    @Override
    public Page<CashWithdrawals> findByPage(Page<CashWithdrawals> page, Long userId, String userName, String mobile, Byte status, String numMin, String numMax, String startTime, String endTime) {
        List<Long> ids = new ArrayList<>();
        Map<Long, UserDto> user = new HashMap<>();
        if(userId != null ||
            StringUtils.isNotBlank(userName) ||
            StringUtils.isNotBlank(mobile)){
            ArrayList<Long> userIds = new ArrayList<>();
            if(userId!=null){
                userIds.add(userId);
            }
            user = userServiceFeign.getBasicUsers(userIds.size() == 0 ? null : userIds,mobile,userName);
            Set<Map.Entry<Long, UserDto>> set = user.entrySet();
            for(Map.Entry<Long, UserDto> map:set){
                ids.add(map.getKey());
            }
            if(CollectionUtils.isEmpty(ids)){
                return page;
            }
        }

        Page<CashWithdrawals> result = cashWithdrawalsMapper.findByPage(page,ids,status,numMin,numMax,startTime,endTime);
        List<CashWithdrawals> records = result.getRecords();
        if (CollectionUtils.isEmpty(records)){
            return result;
        }
        if(CollectionUtils.isEmpty(ids)){
            Set<Long> longs = records.stream().map(CashWithdrawals::getUserId).collect(Collectors.toSet());
            ArrayList<Long> idList = new ArrayList<>(longs);
            user = userServiceFeign.getBasicUsers(idList,mobile,userName);
        }
        buildCashWithdrawals(user,records);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeStatus(CashWithdrawals cashWithdrawals) {
        cache.tryLockAndRun(cashWithdrawals.getId() + "",300,TimeUnit.SECONDS,() -> {

            if (cashWithdrawals.getStatus() == 1){
                CashWithdrawals withdrawals = getById(cashWithdrawals);
                if (withdrawals == null){
                    throw new IllegalArgumentException("订单数据错误");
                }
                withdrawals.setLastUpdateTime(new Date());
                withdrawals.setLastTime(new Date());
                withdrawals.setStatus(cashWithdrawals.getStatus());
                withdrawals.setRemark(cashWithdrawals.getRemark());
                updateById(withdrawals);

                QueryWrapper<Coin> wrapper = new QueryWrapper<>();
                wrapper.eq("name","CNY");
                List<Coin> list = coinService.list(wrapper);
                if (CollectionUtils.isEmpty(list)){
                    throw new IllegalArgumentException("CNY未配置");
                }

                accountService.transfer(withdrawals.getCoinId(),list.get(0).getId(),
                        withdrawals.getUserId(),null,withdrawals.getId(),
                        AccountEnums.withdrawals_out.getCode(),withdrawals.getMum(),
                        withdrawals.getFee(),withdrawals.getRemark());

                //提现的流水
                CashWithdrawAuditRecord cashWithdrawAuditRecord = new CashWithdrawAuditRecord();
                Long userId = Long.parseLong(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
                cashWithdrawAuditRecord.setAuditUserId(userId);
                cashWithdrawAuditRecord.setCreated(new Date());
                cashWithdrawAuditRecord.setOrderId(withdrawals.getId());
                cashWithdrawAuditRecord.setRemark(cashWithdrawals.getRemark());
                cashWithdrawAuditRecord.setStatus((byte)1);
                cashWithdrawAuditRecord.setId(snowflake.nextId());

                //远程调用查询管理员的名称
                Map<Long, AdminUserDto> adminUser = adminUserFegin.getAdminUser(Arrays.asList(userId));
                AdminUserDto adminUserDto = adminUser.get(userId);
                cashWithdrawAuditRecord.setAuditUserName(adminUserDto.getUsername());

                cashWithdrawAuditRecordService.save(cashWithdrawAuditRecord);
            }else{
                //审核 失败的情况

                CashWithdrawals cashWithdrawalsx = getById(cashWithdrawals);
                if (cashWithdrawalsx == null) {
                    throw new IllegalArgumentException("订单数据错误");
                }
                cashWithdrawalsx.setLastTime(new Date());
                cashWithdrawalsx.setStatus(cashWithdrawals.getStatus());
                cashWithdrawalsx.setRemark(cashWithdrawals.getRemark());
                updateById(cashWithdrawalsx);


                //提现的流水
                CashWithdrawAuditRecord cashWithdrawAuditRecord = new CashWithdrawAuditRecord();
                Long userId = Long.parseLong(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
                cashWithdrawAuditRecord.setAuditUserId(userId);
                cashWithdrawAuditRecord.setCreated(new Date());
                cashWithdrawAuditRecord.setOrderId(cashWithdrawalsx.getId());
                cashWithdrawAuditRecord.setRemark(cashWithdrawals.getRemark());
                cashWithdrawAuditRecord.setStatus((byte)1);
                cashWithdrawAuditRecord.setId(snowflake.nextId());

                //远程调用查询管理员的名称
                Map<Long, AdminUserDto> adminUser = adminUserFegin.getAdminUser(Arrays.asList(userId));
                AdminUserDto adminUserDto = adminUser.get(userId);
                cashWithdrawAuditRecord.setAuditUserName(adminUserDto.getUsername());

                cashWithdrawAuditRecordService.save(cashWithdrawAuditRecord);
            }
        });
    }

    @Override
    public void sell(CashWithdrawals cashWithdrawals) {
        //判断手机验证码
        Long userId = Long.parseLong(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
        Map<Long, UserDto> basicUsers = userServiceFeign.getBasicUsers(Arrays.asList(userId), null, null);
        UserDto userDto = basicUsers.get((userId));
        String mobile = userDto.getMobile();
        Object o = redisTemplate.opsForValue().get("SMS:CASH_WITHDRAWS:" + mobile);
        String v = o.toString();
        if (StringUtils.isBlank(v)){
            throw new IllegalArgumentException("验证码过期");
        }
        if (!v.equals(cashWithdrawals.getValidateCode())){
            throw new IllegalArgumentException("验证码错误");
        }

        //判断交易密码
        String paypassword = userDto.getPaypassword();
        if (StringUtils.isBlank(paypassword)){
            throw new IllegalArgumentException("当前用户未设置交易密码，请先设置交易密码");
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(cashWithdrawals.getPayPassword(), paypassword);
        if (!matches){
            throw new IllegalArgumentException("交易密码错误");
        }

        //添加一条提现审核记录
        cashWithdrawals.setUserId(userId);
        cashWithdrawals.setStatus((byte)0);
        Account account = accountService.findUserAccountByCoinName(cashWithdrawals.getCoinId(), null, userId);
        cashWithdrawals.setAccountId(account.getId());
        BigDecimal sellRate = account.getSellRate();
        cashWithdrawals.setMum(sellRate.multiply(cashWithdrawals.getNum()));
        cashWithdrawals.setFee(cashWithdrawals.getNum().subtract(cashWithdrawals.getMum()));

        //设置用户银行卡信息
        Map<Long, UserBankDto> userBank = userBankFeign.getUserBank(Arrays.asList(userId));
        UserBankDto userBankDto = userBank.get(userId);
        System.out.println("userBankDto = " + userBankDto);
        cashWithdrawals.setTruename(userBankDto.getRealName());
        cashWithdrawals.setBank(userBankDto.getBank());
        cashWithdrawals.setBankCard(userBankDto.getBankCard());
        cashWithdrawals.setBankCity(userBankDto.getBankCity());
        cashWithdrawals.setBankProv(userBankDto.getBankProv());
        cashWithdrawals.setBankAddr(userBankDto.getBankAddr());

        cashWithdrawals.setRemark("用户提现");
        cashWithdrawals.setStep((byte)1);
        cashWithdrawals.setCreated(new Date());
        cashWithdrawals.setLastUpdateTime(new Date());
        save(cashWithdrawals);

        accountToLock(cashWithdrawals.getNum(),cashWithdrawals.getCoinId(),userId, DirectionEnums.OUT.getCode());

    }

    private void accountToLock(BigDecimal num, Long coinId, Long userId, int direction) {
        Account account = accountService.findUserAccountByCoinName(coinId, null, userId);
        //冻结 -》可用
        if (direction == DirectionEnums.IN.getCode()){
            if ((account.getFreezeAmount().subtract(num)).compareTo(BigDecimal.ZERO)<0){
                throw new IllegalArgumentException("用户冻结余额不足");
            }
            account.setFreezeAmount(account.getFreezeAmount().subtract(num));
            account.setBalanceAmount(account.getBalanceAmount().add(num));
            accountService.updateById(account);
        }else{// 可用 -》冻结
            if((account.getBalanceAmount().subtract(num)).compareTo(BigDecimal.ZERO)<0){
                throw new IllegalArgumentException("用户余额不足");
            }
            account.setFreezeAmount(account.getFreezeAmount().add(num));
            account.setBalanceAmount(account.getBalanceAmount().subtract(num));
            accountService.updateById(account);
        }
    }

    private void buildCashWithdrawals(Map<Long, UserDto> user, List<CashWithdrawals> records) {
        for(CashWithdrawals CashWithdrawals : records){
            UserDto userDto = user.get(CashWithdrawals.getUserId());
            if(userDto != null){
                CashWithdrawals.setRealName(userDto.getRealName());
                CashWithdrawals.setUsername(userDto.getUsername());
                CashWithdrawals.setMobile(userDto.getMobile());
            }else{
                CashWithdrawals.setRealName("数据为空");
                CashWithdrawals.setUsername("数据为空");
                CashWithdrawals.setMobile("数据为空");
            }
        }
    }
}
